--- id: TASK-003 title: Favorite teams status: "\U0001F3C1 Done" assignee: - '@humdrum-tiv' created_date: '2026-06-16 18:03' updated_date: '2026-06-18 01:17' labels: - feature dependencies: [] priority: high ordinal: 3000 --- ## Description User marks favorite teams. Favorites float to the top of the main dashboard view. In a league view, always show each favorite's last result and next fixture, even outside the current window. ## Acceptance Criteria - [x] #1 favorites pinned to top of dashboard - [x] #2 league view shows favorite last + next - [x] #3 favorites persist ## Implementation Plan Depends on TASK-004 config layer + team identity. 1. model.Team gains ID string (ESPN team id); espn teamJSON.ID + mapTeam set it. 2. App: cfg config.Config + favs map[string]bool (key=league+':'+teamID, fallback abbr). New() loads via config.Load(). Helpers favKey(league,team)/isFav(league,team)/toggleFav(league,team){mutate favs+cfg, config.Save best-effort}. 3. Marking [detail view]: keys Fav(f)=toggle away, FavHome(F)=toggle home. Footer hint. Persist on toggle [AC#3]. 4. Cards: renderCard/teamRow take fav bool -> show ★ before favorite team abbr (components.go). bodyLines passes a.isFav per side. 5. Ordering (sections.go) [AC#1/#2]: - all-leagues: prepend '★ Favorites' section = today's games across leagues involving a favorite; remove those from their per-league section (no dup). - single-league: prepend '★ Favorites' bucket before Today/Upcoming/Past = for each favorite team in league, its live game + most-recent final (last) + next scheduled (next) found within fetched window; dedup. - sections()/bodyLines() invariant preserved (bodyLines re-walks sections()). 6. Out-of-window last/next (favorite's next match >7d away or last >2d ago) deferred to follow-up using ESPN team schedule endpoint -> new task. 7. Tests: favKey stability; favorites-section selection from a fixture set. ## Final Summary Favorite teams: mark, persist, pin to top. What changed: - model.Team gains stable ESPN team ID (captured in espn mapping); favKey = league+':'+id (abbr fallback). - App loads favorites from config on start; toggleFav persists immediately via config.Save (best-effort) [AC#3]. - Marking: in detail view, f toggles the away team, F the home team; footer hints added. ★ shown next to favorite teams on dashboard cards and in the detail score box. - Ordering [AC#1/#2]: * All-leagues dashboard: a '★ Favorites' section pinned on top with today's games involving a favorite, removed from their league section (no dupes). * Single-league view: a '★ Favorites' bucket above Today/Upcoming/Past showing each favorite's live game + most-recent final (last) + next scheduled fixture (next), drawn from the fetched window. Scope note: last/next resolve within the per-league fetch window; matches further out are deferred to TASK-009 (per-team schedule endpoint). Also in this branch (window infra): per-league fetch windows on model.League (MLB 3/5d, NFL 10/10d, soccer/default 5/10d) and a required &limit=300 on ScoreboardRange — the latter also fixes a latent bug where dense slates were silently truncated to ESPN's default cap. Tests: ui/favorites_test.go (favKey identity, hasFav, favoriteHighlights live/next/last selection + dedup + non-fav exclusion). All packages build/vet/test clean.